package com.twq.oo.classes

/**
 * Created by tangweiqun on 2017/11/28.
 * 1、使用abstract来定义一个抽象类
 */
abstract class Element {
  //2、定义一个抽象方法(没有实现)
  def contents: Array[String]

  //3、定义没有括号的具体方法
  //3.1、可以使用val代替def，val的话调用的性能比较好，因为类初始化的时候就已经计算好了，但是val占内存，而def不占内存
  //3.2、在scala中，如果是没有参数的方法，通常都是将()去掉，但是含有side effect的方法除外，比如println()，最好带上()
  def width: Int = contents(0).length

  def height: Int = contents.length

  def demo() = {
    println("Element's implementation invoked")
  }

  import Element._

  //8、定义方法above、beside、toString等
  def above(that: Element): Element = {
    val this1 = this widen that.width
    val that1 = that widen this.width
    elem(this1.contents ++ that1.contents)
  }

  def beside(that: Element): Element = {
    val this1 = this heighten that.height
    val that1 = that heighten this.height
    elem(
      for ((line1, line2) <- this1.contents zip that1.contents)
        yield line1 + line2)
  }

  //10、定义widen和heighten方法
  def widen(w: Int): Element =
    if (w <= width) this
    else {
      val left = elem(' ', (w - width) / 2, height)
      val right = elem(' ', w - width - left.width, height)
      left beside this beside right
    }

  def heighten(h: Int): Element =
    if (h <= height) this
    else {
      val top = elem(' ', width, (h - height) / 2)
      val bot = elem(' ', width, h - height - top.height)
      top above this above bot
    }

  override def toString = contents mkString "\n"
}

object Element {

  //4、使用extends来继承抽象类，使得可以实例化
  //除了在构造器中使用val contents来重写抽象方法，我们还可以在类里使用val 变量来重写抽象方法
  class ArrayElement(
                      val contents: Array[String]
                    ) extends Element {
    override def demo() = {
      println("ArrayElement's implementation invoked")
    }

    //5.2、使用final来修饰方法，使得这个方法不能被子类重写
    //final也可以修饰类，使得修饰的这个类不能被继承
    final def testFinal() = println("test final")
  }

  //5、调用父类的构造器
  class LineElementOld(s: String) extends ArrayElement(Array(s)) {
    override def width = s.length

    override def height = 1

    //5.1、使用关键词override，必须修饰父类里有的成员
    //override def high = 1 //编译不过

    override def demo() = {
      println("LineElement's implementation invoked")
    }
  }

  //7、使用符合代替继承
  private class LineElement(s: String) extends Element {
    val contents = Array(s)

    override def width = s.length

    override def height = 1
  }

  private class UniformElement(
                                ch: Char,
                                override val width: Int,
                                override val height: Int
                              ) extends Element {
    private val line = ch.toString * width

    def contents = Array.fill(height)(line)
  }

  //6、多态和动态绑定
  val e1: Element = new ArrayElement(Array("hello", "world"))
  val ae: LineElementOld = new LineElementOld("hello")
  val e2: Element = ae
  val e3: Element = new UniformElement('x', 2, 3)
  invokeDemo(e1)
  invokeDemo(ae)
  invokeDemo(e3)

  def invokeDemo(e: Element) = {
    e.demo()
  }

  //9、定义工厂方法
  def elem(contents: Array[String]): Element =
    new ArrayElement(contents)

  def elem(chr: Char, width: Int, height: Int): Element =
    new UniformElement(chr, width, height)

  def elem(line: String): Element =
    new LineElement(line)
}

//11、整合
object Spiral {

  import Element._

  val space = elem(" ")
  val corner = elem("+")

  def spiral(nEdges: Int, direction: Int): Element = {
    if (nEdges == 1)
      elem("+")
    else {
      val sp = spiral(nEdges - 1, (direction + 3) % 4)

      def verticalBar = elem('|', 1, sp.height)

      def horizontalBar = elem('-', sp.width, 1)

      if (direction == 0)
        (corner beside horizontalBar) above (sp beside space)
      else if (direction == 1)
        (sp above space) beside (corner above verticalBar)
      else if (direction == 2)
        (space beside sp) above (horizontalBar beside corner)
      else
        (verticalBar above corner) beside (space above sp)
    }
  }

  def main(args: Array[String]) = {
    val nSides = args(0).toInt
    println(spiral(nSides, 0))
  }
}
